package com.allwees.bs.c.module.order.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.allwees.bs.c.module.order.constant.EOrderQueryType;
import com.allwees.bs.c.module.order.context.event.InviteeFirstPayedOrderEvent;
import com.allwees.bs.c.module.order.context.event.OrderPayedEvent;
import com.allwees.bs.c.module.order.context.event.OrderReviewedEvent;
import com.allwees.bs.c.module.order.context.processor.OrderPostProcessor;
import com.allwees.bs.c.module.order.context.validator.OrderPayPreValidator;
import com.allwees.bs.c.module.order.dto.LiveOrderDTO;
import com.allwees.bs.c.module.order.dto.OrderDTO;
import com.allwees.bs.c.module.order.dto.OrderItemDTO;
import com.allwees.bs.c.module.order.dto.TrackingDetailDTO;
import com.allwees.bs.c.module.order.entity.LogisticsWaybillEntity;
import com.allwees.bs.c.module.order.entity.OrderEntity;
import com.allwees.bs.c.module.order.entity.OrderItemEntity;
import com.allwees.bs.c.module.order.entity.OrderReviewItemEntity;
import com.allwees.bs.c.module.order.entity.OrderSpecification;
import com.allwees.bs.c.module.order.mapper.OrderItemMapper;
import com.allwees.bs.c.module.order.mapper.OrderMapper;
import com.allwees.bs.c.module.order.mq.event.OrderItemReviewedMqEvent;
import com.allwees.bs.c.module.order.mq.event.OrderPayedMqEvent;
import com.allwees.bs.c.module.order.repository.LogisticsWaybillEntityRepository;
import com.allwees.bs.c.module.order.repository.OrderFlowRepository;
import com.allwees.bs.c.module.order.repository.OrderItemRepository;
import com.allwees.bs.c.module.order.repository.OrderRepository;
import com.allwees.bs.c.module.order.repository.OrderReviewItemRepository;
import com.allwees.bs.c.module.order.req.OrderCancelReq;
import com.allwees.bs.c.module.order.req.OrderReviewReq;
import com.allwees.bs.c.module.order.service.IOrderService;
import com.allwees.bs.c.module.order.util.OrderFlowUtil;
import com.allwees.bs.c.module.user.entity.UserEntity;
import com.allwees.bs.c.module.user.repository.UserRepository;
import com.allwees.bs.core.model.order.wf.order.EOrderAction;
import com.allwees.bs.core.model.order.wf.order.EOrderState;
import com.allwees.bs.core.model.payment.enums.EPaymentType;
import com.allwees.bs.core.modelbase.constant.ResultEnum;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import com.allwees.bs.core.modelbase.page.PageParams;
import com.allwees.bs.core.modelbase.page.PaginateDTO;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.util.DateUtil;
import com.allwees.core.common.util.StringUtil;
import com.allwees.core.common.util.freemarker.FreeMarkerUtil;
import com.allwees.core.mq.rabbitmq.MqUtil;
import com.allwees.sender.EMailSender;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
    @Autowired
    private OrderPayPreValidator orderPayPreValidator;
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private OrderFlowRepository orderFlowRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderReviewItemRepository orderReviewItemRepository;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private OrderItemRepository orderItemRepository;
    @Autowired
    private OrderPostProcessor orderPostProcessor;
    @Autowired
    private LogisticsWaybillEntityRepository orderLogisticsRepository;
    @Autowired
    protected EMailSender mailSender;
    @Autowired
    protected FreeMarkerUtil freeMarkerUtil;

    @Override
    public PaginateDTO<OrderDTO> fetchUserOrders(String userUuid, EOrderQueryType queryType, Integer type, PageParams pageParams) {
        EOrderState[] state = queryType != null ? queryType.getStates() : null;

        Page<OrderEntity> orders = this.orderRepository.findAll(OrderSpecification.specification(userUuid, state, type),
                pageParams.toPageable(Sort.Direction.DESC, "createdAt"));

        PaginateDTO<OrderDTO> paginateDTO = PaginateDTO.of(orders.getTotalElements(), orders.getContent().size(), pageParams);
        paginateDTO.setResults(this.orderMapper.toDtos(orders.getContent()));
        return paginateDTO;
    }

    @Override
    public OrderDTO fetchUserOrder(String userUuid, String orderUuid) {
        OrderEntity order = _checkOrder(userUuid, orderUuid);
        OrderDTO orderDTO = this.orderMapper.toDto(order);
        orderDTO.setOriginalAmtShipping(order.getAmtShippingAfterAdd());
        return orderDTO;
    }

    @Override
    public TrackingDetailDTO queryTrackingDetail(String userUuid, String orderItemUuid) {
        LogisticsWaybillEntity entity = orderLogisticsRepository.findByOrderItemUuid(orderItemUuid);
        if (entity == null) {
            return null;
        }
        TrackingDetailDTO trackingDetailDTO = new TrackingDetailDTO();
        trackingDetailDTO.setCode(entity.getStatus());
        trackingDetailDTO.setDelivered(entity.getDelivered());
        trackingDetailDTO.setDeliverDate(entity.getDeliverDate());
        trackingDetailDTO.setTrackingNumber(entity.getExpressCode());

        if (entity.getTrackingDetail() != null) {
            trackingDetailDTO.setData(JSONArray.parseObject(entity.getTrackingDetail(), JSONArray.class));
        }
        return trackingDetailDTO;
    }


    @Override
    @Transactional
    public OrderDTO userConfirmShippingOrder(String userUuid, String orderItemUuid) {

        OrderItemEntity orderItemEntity = checkOrderItem(userUuid, orderItemUuid);
        OrderEntity order = _checkOrder(userUuid, orderItemEntity.getOrderUuid());

        //只有已妥投状态才能确认收货
       /* if (!EOrderState.SHIPPED.equals(order.getState())) {
            throw BusinessException.getInstance(ResultEnum.ORDER_CANNOT_CONFIRM_SHIPPING);
        }*/

       // order.signal(EOrderAction.USER_CONFIRM_RECEIVED);
        //order.setReceiveAt(new Date());
        //orderRepository.save(order);

        orderPostProcessor.userReceivedProcess(orderItemUuid);

        return this.orderMapper.toDto(orderItemUuid);
    }

    @Override
    @Transactional
    public OrderDTO userCancelOrder(String userUuid, String orderUuid, OrderCancelReq req) {
        OrderEntity order = _checkOrder(userUuid, orderUuid);

        //只有待支付状态可取消订单
        if (!EOrderState.CREATED.equals(order.getState())) {
            throw BusinessException.getInstance(ResultEnum.ORDER_CANNOT_CANCEL_PAID);
        }
        //记录订单的状态和最后的动作
        order.signal(EOrderAction.USER_CANCEL);
        order.setClosedAt(new Date());

        //记录订单流转
        orderRepository.save(order);
        orderFlowRepository.save(OrderFlowUtil.generate(order));

        // 返还库存
        orderPostProcessor.userCancelProcess(orderUuid);

        return this.orderMapper.toDto(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderDTO payOrderByCOD(OrderEntity order) {
        if (!EOrderState.CREATED.equals(order.getState())) {
            log.error("订单重复付款->订单ID：{}", order.getId());
            return orderMapper.toDto(order);
        }
        List<OrderItemEntity> orderItems = orderItemRepository.findOrderItems(order.getUuid());
        orderPayPreValidator.validate(orderItems,order);
        order.setIsInfluencer(userRepository.checkIsInfluencer(order.getUserUuid()));
        Date now = DateUtil.now();
        order.setBackState(order.getState());
        order.signal(EOrderAction.USER_COD);
        //记录进入待发货时间
        order.setShippingWaitAt(now);
        order.setPaymentType(EPaymentType.COD);
        order.setPaymentAt(null);
        order.setPaymentUuid(StringUtil.EMPTY);
        order.setPayed(false);
        order.setStatus(EStatus.OMS_WAIT_MERCHANT_SHIP.value());

        this.orderRepository.save(order);
        this.orderFlowRepository.save(OrderFlowUtil.generate(order));
        //订单项处理
        orderPostProcessor.userPayedProcess(orderItems, now, true);
        OrderDTO orderDTO = this.orderMapper.toDto(order);
        applicationContext.publishEvent(new OrderPayedEvent(orderDTO));
        //生成OMS订单
        MqUtil.send(new OrderPayedMqEvent(order.getUuid()));

        return orderDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderDTO payOrder(String orderUuid, String paymentUuid, EPaymentType paymentType) {
        OrderEntity order = this.orderRepository.getByUuid(orderUuid);
        if (order == null) {
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }

        if (!EOrderState.CREATED.equals(order.getState())) {
            log.error("订单重复付款->订单ID：{}", order.getId());
            return orderMapper.toDto(order);
        }

        List<OrderItemEntity> orderItems = orderItemRepository.findOrderItems(orderUuid);
        orderPayPreValidator.validate(orderItems,order);
        order.setIsInfluencer(userRepository.checkIsInfluencer(order.getUserUuid()));
        Date now = DateUtil.now();
        order.setBackState(order.getState());

        order.signal(EOrderAction.USER_PAYED);
        //记录进入代待货时间
        order.setShippingWaitAt(now);

        order.setPaymentType(paymentType);
        order.setPaymentAt(now);
        order.setPaymentUuid(paymentUuid);
        order.setPayed(true);

        this.orderRepository.save(order);
        this.orderFlowRepository.save(OrderFlowUtil.generate(order));

        //订单项处理
        orderPostProcessor.userPayedProcess(orderItems, now, false);

        OrderDTO orderDTO = this.orderMapper.toDto(order);
        // applicationContext.publishEvent(new OrderSyncToErpEvent(orderUuid));
        applicationContext.publishEvent(new OrderPayedEvent(orderDTO));
        MqUtil.send(new OrderPayedMqEvent(orderUuid));
        return orderDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderItemDTO reviewOrderItem(String userUuid, OrderReviewReq orderReviewReq) {
        OrderItemEntity orderItem = orderItemRepository.getByUuid(orderReviewReq.getOrderItemUuid());
        if (Objects.isNull(orderItem)) {
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }
        OrderEntity order = _checkOrder(userUuid, orderReviewReq.getOrderUuid());
        if (DateUtil.nowTime() > orderItem.getReviewExpiredAt().getTime()) {
            throw BusinessException.getInstance(ResultEnum.ORDER_NOT_REVIEWABLE);
        }
        if (orderItem.getReviewAt() != null) {
            throw BusinessException.getInstance(ResultEnum.ORDER_REPEAT_REVIEW);
        }

        OrderReviewItemEntity orderReviewItem = new OrderReviewItemEntity(orderReviewReq, orderItem, userRepository.getByUuid(userUuid));
        orderReviewItemRepository.save(orderReviewItem);

        //更新订单项信息
        orderPostProcessor.userReviewedProcess(order, orderItem);

        MqUtil.send(new OrderItemReviewedMqEvent(orderReviewReq.getOrderItemUuid()));
        applicationContext.publishEvent(new OrderReviewedEvent(orderReviewItem));

        return orderItemMapper.toDto(orderItem);
    }

    @Override
    public OrderItemDTO findOrderItem(String userUuid, String orderUuid, String orderItemUuid) {
        Assert.hasLength(orderUuid, "order id is not be blank");
        Assert.hasLength(orderItemUuid, "order item id is not be blank");

        _checkOrder(userUuid, orderUuid);

        OrderItemEntity item = orderItemRepository.getByUuid(orderItemUuid);
        if (item == null) {
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }
        return orderItemMapper.toDto(item);
    }

    @Override
    public LiveOrderDTO fetchLiveOrder(String productUuid) {
        OrderEntity order = null;
        //只查询最新2小时的订单
        Date afterTime = new Date(DateUtil.nowTime() - 2 * 60 * 60 * 1000);
        List<OrderEntity> orderList = this.orderRepository
                .findLatestOrder(afterTime, PageRequest.of(0, 1, Sort.Direction.DESC, "createdAt"));
        if (orderList != null && !orderList.isEmpty()) {
            order = orderList.get(0);
        }

        if (order != null) {
            return new LiveOrderDTO(order.getShippingAddressFirstName(), order.getMainPhoto(), order.getCreatedAt());
        } else {
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void check4UserFirstPayedOrder(OrderDTO order) {
        UserEntity user = userRepository.getByUuid(order.getUserUuid());
        if (!user.isHasOrder()) {
            user.setFirstPaymentAt(order.getPaymentAt());
            userRepository.save(user);
            if (StringUtils.isNotBlank(user.getInviterUuid())) {
                applicationContext.publishEvent(new InviteeFirstPayedOrderEvent(user.getInviterUuid()));
            }
        }
    }

    private OrderItemEntity checkOrderItem(String userUuid, String orderItemUuid) {
        OrderItemEntity orderItem = this.orderItemRepository.getByUuid(orderItemUuid);
        if (orderItem == null) {
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }
        if (!orderItem.getUserUuid().equals(userUuid)) {
            throw BusinessException.getInstance(ResultEnum.DATA_NO_PRIVILEGE);
        }
        return orderItem;
    }

    private OrderEntity _checkOrder(String userUuid, String orderUuid) {
        OrderEntity order = this.orderRepository.getByUuid(orderUuid);
        if (order == null) {
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }
        if (!order.getUserUuid().equals(userUuid)) {
            throw BusinessException.getInstance(ResultEnum.DATA_NO_PRIVILEGE);
        }
        return order;
    }

    @Override
    public void sendOrderConfirmEmail(String orderUuid) {

        OrderEntity order = orderRepository.getByUuid(orderUuid);
        OrderDTO orderDTO = this.orderMapper.toSimpleDto(order);

        UserEntity user = userRepository.getByUuid(order.getUserUuid());

        Map<String, Object> params = new HashMap<>(4);
        params.put("userName", String.format("%s%s", user.getFirstName(), user.getLastName()));
        params.put("order", orderDTO);

        String content = freeMarkerUtil.render("email_order_confirm.ftl", params);
        mailSender.send(user.getEmail(), "Your Allwees order confirmation ", content, true);
    }

    @Override
    public void updateRefundChannelIdByOrderUuid(String orderUuid, String refundChannelId) {
        OrderEntity order = orderRepository.getByUuid(orderUuid);
        order.setRefundChannelUuid(refundChannelId);
        orderRepository.save(order);
    }
}
